﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace TeaCompiler.Parser {

    /// <summary>
    /// 表示书写代码的工具。
    /// </summary>
    public class CodeWriter :IDisposable {

        #region 配置

        /// <summary>
        /// 配置文件内表示缩进的标记。不要随便改变。
        /// </summary>
        const string INDENTCHARS = "\t";

        /// <summary>
        /// 配置文件内表示隔行的标记。不要随便改变。
        /// </summary>
        const string NEWLINE = "\r\n";

        /// <summary>
        /// 缩略字符串的缓存数。
        /// </summary>
        const int CATHESIZE = 7;

        #endregion

        #region 私有变量

        /// <summary>
        /// 使用的文件流。
        /// </summary>
        protected TextWriter txtWriter;

        /// <summary>
        /// 当前的缩进数。
        /// </summary>
        protected int indents = 0;

        /// <summary>
        /// 缩略字符串的缓存。
        /// </summary>
        string[] _indentCe;

        #endregion

        #region 属性

        /// <summary>
        /// 返回用来写输出的 <see cref="T:System.Text.Encoding"/>。
        /// </summary>
        /// <returns>用来写入输出的 Encoding。</returns>
        public virtual Encoding Encoding {
            get { return txtWriter.Encoding; }
        }

        /// <summary>
        /// 获取当前的缩进数。
        /// </summary>
        public int Indents {
            get { return indents; }
        }

        /// <summary>
        /// 获取或设置由当前 TextWriter 使用的行结束符字符串。
        /// </summary>
        /// <returns>当前 TextWriter 的行结束符字符串。</returns>
        public string NewLine {
            get {
                return txtWriter.NewLine;
            }
            set {
                txtWriter.NewLine = value;
            }
        }

        /// <summary>
        /// 获取和设置缩进用字符串。
        /// </summary>
        public string IndentChars { get; set; }

        /// <summary>
        /// 返回当前缩进量的字符串。
        /// </summary>
        protected string IndentString {
            get {
                if (indents <= CATHESIZE) return _indentCe[indents];
                int i = indents - CATHESIZE;
                StringBuilder r = new StringBuilder(_indentCe[CATHESIZE], i * IndentChars.Length);
                while (i-- > 0)
                    r.Append(IndentChars);
                return r.ToString();
            }
        }


        #endregion

        #region 基本

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="p">项目。</param>
        /// <param name="stream">流。</param>
        public CodeWriter(string newLine, string indentChars, Stream stream)
            : this(newLine, indentChars, new StreamWriter(stream)) {
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="p">项目。</param>
        /// <param name="path">文件的物理位置。</param>
        public CodeWriter(string newLine, string indentChars, string path)
            : this(newLine, indentChars, new StreamWriter(path)) {
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="p">项目。</param>
        /// <param name="path">文件的物理位置。</param>
        /// <param name="encoding">文件编码。</param>
        public CodeWriter(string newLine, string indentChars, string path, Encoding encoding)
            : this(newLine, indentChars, new StreamWriter(path, false, encoding)) {
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="sb">StringBuilder。</param>
        public CodeWriter(TextWriter sb) {
            txtWriter = sb;
            NewLine = NEWLINE;
            IndentChars = INDENTCHARS;
            InitIndents();
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="filePath">位置。</param>
        public CodeWriter(string filePath)
            : this(new StreamWriter(filePath)) {

        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="sb">StringBuilder。</param>
        public CodeWriter(StringBuilder sb)
            : this(new StringWriter(sb)) {
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="p">项目。</param>
        /// <param name="sb">StringBuilder。</param>
        public CodeWriter(string newLine, string indentChars, TextWriter sb) {
            txtWriter = sb;
            NewLine = newLine;
            IndentChars = indentChars;
            InitIndents();
        }

        /// <summary>
        /// 初始化 Play.Site.Designer.CodeWriter 的新实例。
        /// </summary>
        /// <param name="p">项目。</param>
        /// <param name="sb">StringBuilder。</param>
        public CodeWriter(string newLine, string indentChars, StringBuilder sb)
            : this(newLine, indentChars, new StringWriter(sb)) {

        }

        /// <summary>
        /// 增加缩进。
        /// </summary>
        public void Indent() {
            indents++;
        }

        /// <summary>
        /// 减小缩进。
        /// </summary>
        public void UnIndent() {
            indents--;
        }

        /// <summary>
        /// 初始化缩进信息，由构造函数调用。
        /// </summary>
        void InitIndents() {
            _indentCe = new string[CATHESIZE + 1];
            _indentCe[0] = String.Empty;
            for (int i = 1; i <= CATHESIZE; i++)
                _indentCe[i] = _indentCe[i - 1] + IndentChars;
        }

        /// <summary>
        /// 关闭当前编写器并释放任何与该编写器关联的系统资源。
        /// </summary>
        public void Close() {
            txtWriter.Close();
        }

        /// <summary>
        /// 释放由此 <see cref="T:System.IO.TextWriter"/> 对象使用的所有资源。
        /// </summary>
        public virtual void Dispose() {
            txtWriter.Dispose();
        }

        /// <summary>
        /// 清理当前编写器的所有缓冲区，使所有缓冲数据写入基础设备。
        /// </summary>
        public void Flush() {
            txtWriter.Flush();
        }

        #endregion

        #region 公共

        /// <summarytxtWriter  /// 写入当前缩进量。
        /// </summary>
        public void WriteIndent() {
            if (indents < CATHESIZE)
                txtWriter.Write(_indentCe[indents]);
            else
                txtWriter.Write(IndentString);
        }

        /// <summary>
        /// 写入一个代码。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        /// <param name="args">参数。</param>
        public void WriteLine(string value, params object[] args) {
            Write(String.Format(value, args));
            txtWriter.WriteLine();
        }

        /// <summary>
        /// 写入一个代码。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteLine(string value) {
            Write(value);
            txtWriter.WriteLine();
        }

        /// <summary>
        /// 写入一个空行。
        /// </summary>
        public void WriteLine() {
            txtWriter.WriteLine();
        }

        /// <summary>
        /// 写入一个用来分隔的行。 比如两个对象间的空行
        /// </summary>
        public virtual void WriteSperatorLine() {
            txtWriter.WriteLine();
        }

        /// <summary>
        /// 写入一个代码。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        /// <param name="args">参数。</param>
        public void Write(string value, params object[] args) {
            Write(String.Format(value, args));
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(string value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(char value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(int value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(object value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(long value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 在当前流写入一个字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public virtual void Write(ulong value) {
            txtWriter.Write(value);
        }

        /// <summary>
        /// 写入一个空格，然后写入指定的字符串。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteWithWhitespace(string value) {
            txtWriter.Write(' ');
            txtWriter.Write(value);
        }

        /// <summary>
        /// 写入单独一行的字符串，并在前面写入缩进。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteSingleLine(string value) {
            txtWriter.WriteLine();
            txtWriter.Write(IndentString);
            txtWriter.Write(value);
        }

        /// <summary>
        /// 写入单独一行的字符串，并在前面写入缩进。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteSingleLine(string value1, string value2, string value3 = null) {
            txtWriter.WriteLine();
            txtWriter.Write(IndentString);
            txtWriter.Write(value1);
            txtWriter.Write(value2);
            txtWriter.Write(value3);
        }

        /// <summary>
        /// 写入单独一行的字符串，并在前面写入缩进。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteSingleLine(params string[] args) {
            txtWriter.WriteLine();
            txtWriter.Write(IndentString);
            foreach (string s in args)
                txtWriter.Write(s);
        }

        /// <summary>
        /// 写入单独一行的字符串，并在前面写入缩进。
        /// </summary>
        /// <param name="value">要写入的内容。</param>
        public void WriteMultiLine(string value) {
            txtWriter.Write(IndentString);
            foreach (char c in value) {
                txtWriter.Write(c);
                if (c == '\n') {
                    WriteIndent();
                }
            }
            txtWriter.WriteLine();
        }

        #endregion

    }


}
